/******************************************************************************* * Copyright (c) 2004, 2010 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * Chris Gross chris.gross@us.ibm.com Bug 107443 *******************************************************************************/ package org.eclipse.ui.internal; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.ListenerList; import org.eclipse.core.runtime.Status; import org.eclipse.jface.action.ContributionItem; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.util.Geometry; import org.eclipse.osgi.util.NLS; //import org.eclipse.swt.graphics.Cursor; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.IMemento; import org.eclipse.ui.IPersistable; import org.eclipse.ui.IPropertyListener; import org.eclipse.ui.IWorkbenchPartReference; import org.eclipse.ui.IWorkbenchPreferenceConstants; //import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.XMLMemento; import org.eclipse.ui.internal.StartupThreading.StartupRunnable; //import org.eclipse.ui.internal.dnd.AbstractDropTarget; //import org.eclipse.ui.internal.dnd.DragUtil; //import org.eclipse.ui.internal.dnd.IDropTarget; import org.eclipse.ui.internal.dnd.SwtUtil; import org.eclipse.ui.internal.intro.IIntroConstants; import org.eclipse.ui.internal.layout.ITrimManager; import org.eclipse.ui.internal.layout.IWindowTrim; import org.eclipse.ui.internal.presentations.PresentablePart; import org.eclipse.ui.internal.presentations.PresentationFactoryUtil; import org.eclipse.ui.internal.presentations.PresentationSerializer; import org.eclipse.ui.internal.util.PrefUtil; import org.eclipse.ui.internal.util.Util; import org.eclipse.ui.presentations.AbstractPresentationFactory; import org.eclipse.ui.presentations.IPresentablePart; import org.eclipse.ui.presentations.IStackPresentationSite; //import org.eclipse.ui.presentations.StackDropResult; import org.eclipse.ui.presentations.StackPresentation; /** * Implements the common behavior for stacks of Panes (ie: EditorStack and ViewStack) * This layout container has PartPanes as children and belongs to a PartSashContainer. * */ public abstract class PartStack extends LayoutPart implements ILayoutContainer { public static final int PROP_SELECTION = 0x42; private List children = new ArrayList(3); private boolean isActive = true; private ArrayList presentableParts = new ArrayList(); private Map properties = new HashMap(); protected int appearance = PresentationFactoryUtil.ROLE_VIEW; /** * Stores the last value passed to setSelection. If UI updates are being deferred, * this may be significantly different from the other current pointers. Once UI updates * are re-enabled, the stack will update the presentation selection to match the requested * current pointer. */ private LayoutPart requestedCurrent; /** * Stores the current part for the stack. Whenever the outside world asks a PartStack * for the current part, this is what gets returned. This pointer is only updated after * the presentation selection has been restored and the stack has finished updating its * internal state. If the stack is still in the process of updating the presentation, * it will still point to the previous part until the presentation is up-to-date. */ private LayoutPart current; /** * Stores the presentable part sent to the presentation. Whenever the presentation * asks for the current part, this is what gets returned. This is updated before sending * the part to the presentation, and it is not updated while UI updates are disabled. * When UI updates are enabled, the stack first makes presentationCurrent match * requestedCurrent. Once the presentation is displaying the correct part, the "current" * pointer on PartStack is updated. */ private PresentablePart presentationCurrent; private boolean ignoreSelectionChanges = false; protected IMemento savedPresentationState = null; protected DefaultStackPresentationSite presentationSite = new DefaultStackPresentationSite() { public void close(IPresentablePart part) { PartStack.this.close(part); } public void close(IPresentablePart[] parts) { PartStack.this.close(parts); } public void dragStart(IPresentablePart beingDragged, Point initialLocation, boolean keyboard) { PartStack.this.dragStart(beingDragged, initialLocation, keyboard); } public void dragStart(Point initialLocation, boolean keyboard) { PartStack.this.dragStart(null, initialLocation, keyboard); } public boolean isPartMoveable(IPresentablePart part) { return PartStack.this.isMoveable(part); } public void selectPart(IPresentablePart toSelect) { PartStack.this.presentationSelectionChanged(toSelect); } public boolean supportsState(int state) { return PartStack.this.supportsState(state); } public void setState(int newState) { PartStack.this.setState(newState); } public IPresentablePart getSelectedPart() { return PartStack.this.getSelectedPart(); } public void addSystemActions(IMenuManager menuManager) { PartStack.this.addSystemActions(menuManager); } public boolean isStackMoveable() { return canMoveFolder(); } public void flushLayout() { PartStack.this.flushLayout(); } public IPresentablePart[] getPartList() { List parts = getPresentableParts(); return (IPresentablePart[]) parts.toArray(new IPresentablePart[parts.size()]); } public String getProperty(String id) { // return PartStack.this.getProperty(id); // RAP [hs] patched for interactiondesign API String result = PartStack.this.getProperty(id); if( id.equals( "id" ) ) { result = PartStack.this.getID(); } return result; } }; // RAP [rh] PartStackDropResult disabled since DnD support is missing // private static final class PartStackDropResult extends AbstractDropTarget { // private PartPane pane; // // // Result of the presentation's dragOver method or null if we are stacking over the // // client area of the pane. // private StackDropResult dropResult; // private PartStack stack; // // /** // * Resets the target of this drop result (allows the same drop result object to be // * reused) // * // * @param stack // * @param pane // * @param result result of the presentation's dragOver method, or null if we are // * simply stacking anywhere. // * @since 3.1 // */ // public void setTarget(PartStack stack, PartPane pane, StackDropResult result) { // this.pane = pane; // this.dropResult = result; // this.stack = stack; // } // // public void drop() { // // If we're dragging a pane over itself do nothing // //if (dropResult.getInsertionPoint() == pane.getPresentablePart()) { return; }; // // Object cookie = null; // if (dropResult != null) { // cookie = dropResult.getCookie(); // } // // // Handle cross window drops by opening a new editor // if (pane instanceof EditorPane) { // if (pane.getWorkbenchWindow() != stack.getWorkbenchWindow()) { // EditorPane editor = (EditorPane) pane; // try { // IEditorInput input = editor.getEditorReference().getEditorInput(); // // // Close the old editor and capture the actual closed state incase of a 'cancel' // boolean editorClosed = editor.getPage().closeEditor(editor.getEditorReference(), true); // // // Only open open the new editor if the old one closed // if (editorClosed) // stack.getPage().openEditor(input, editor.getEditorReference().getId()); // return; // } catch (PartInitException e) { // e.printStackTrace(); // } // // } // } // // if (pane.getContainer() != stack) { // // Moving from another stack // stack.derefPart(pane); // pane.reparent(stack.getParent()); // stack.add(pane, cookie); // stack.setSelection(pane); // pane.setFocus(); // } else if (cookie != null) { // // Rearranging within this stack // stack.getPresentation().movePart(stack.getPresentablePart(pane), cookie); // } // } // // public Cursor getCursor() { // return DragCursors.getCursor(DragCursors.CENTER); // } // // public Rectangle getSnapRectangle() { // if (dropResult == null) { // return DragUtil.getDisplayBounds(stack.getControl()); // } // return dropResult.getSnapRectangle(); // } // } // RAP [rh] unused code: PartStackDropResult is disabled // private static final PartStackDropResult dropResult = new PartStackDropResult(); protected boolean isMinimized; private ListenerList listeners = new ListenerList(); /** * Custom presentation factory to use for this stack, or null to * use the default */ private AbstractPresentationFactory factory; private boolean smartZoomed = false; private boolean doingUnzoom = false; protected abstract boolean isMoveable(IPresentablePart part); protected abstract void addSystemActions(IMenuManager menuManager); protected abstract boolean supportsState(int newState); protected abstract boolean canMoveFolder(); protected abstract void derefPart(LayoutPart toDeref); protected abstract boolean allowsDrop(PartPane part); protected static void appendToGroupIfPossible(IMenuManager m, String groupId, ContributionItem item) { try { m.appendToGroup(groupId, item); } catch (IllegalArgumentException e) { m.add(item); } } /** * Creates a new PartStack, given a constant determining which presentation to use * * @param appearance one of the PresentationFactoryUtil.ROLE_* constants */ public PartStack(int appearance) { this(appearance, null); } /** * Creates a new part stack that uses the given custom presentation factory * @param appearance * @param factory custom factory to use (or null to use the default) */ public PartStack(int appearance, AbstractPresentationFactory factory) { super("PartStack"); //$NON-NLS-1$ this.appearance = appearance; this.factory = factory; } /** * Adds a property listener to this stack. The listener will receive a PROP_SELECTION * event whenever the result of getSelection changes * * @param listener */ public void addListener(IPropertyListener listener) { listeners.add(listener); } public void removeListener(IPropertyListener listener) { listeners.remove(listener); } protected final boolean isStandalone() { return (appearance == PresentationFactoryUtil.ROLE_STANDALONE || appearance == PresentationFactoryUtil.ROLE_STANDALONE_NOTITLE); } /** * Returns the currently selected IPresentablePart, or null if none * * @return */ protected IPresentablePart getSelectedPart() { return presentationCurrent; } protected IStackPresentationSite getPresentationSite() { return presentationSite; } /** * Tests the integrity of this object. Throws an exception if the object's state * is invalid. For use in test suites. */ public void testInvariants() { Control focusControl = Display.getCurrent().getFocusControl(); boolean currentFound = false; LayoutPart[] children = getChildren(); for (int idx = 0; idx < children.length; idx++) { LayoutPart child = children[idx]; // No null children allowed Assert.isNotNull(child, "null children are not allowed in PartStack"); //$NON-NLS-1$ // This object can only contain placeholders or PartPanes Assert.isTrue(child instanceof PartPlaceholder || child instanceof PartPane, "PartStack can only contain PartPlaceholders or PartPanes"); //$NON-NLS-1$ // Ensure that all the PartPanes have an associated presentable part IPresentablePart part = getPresentablePart(child); if (child instanceof PartPane) { Assert.isNotNull(part, "All PartPanes must have a non-null IPresentablePart"); //$NON-NLS-1$ } // Ensure that the child's backpointer points to this stack ILayoutContainer childContainer = child.getContainer(); // Disable tests for placeholders -- PartPlaceholder backpointers don't // obey the usual rules -- they sometimes point to a container placeholder // for this stack instead of the real stack. if (!(child instanceof PartPlaceholder)) { if (isDisposed()) { // Currently, we allow null backpointers if the widgetry is disposed. // However, it is never valid for the child to have a parent other than // this object if (childContainer != null) { Assert .isTrue(childContainer == this, "PartStack has a child that thinks it has a different parent"); //$NON-NLS-1$ } } else { // If the widgetry exists, the child's backpointer must point to us Assert .isTrue(childContainer == this, "PartStack has a child that thinks it has a different parent"); //$NON-NLS-1$ // If this child has focus, then ensure that it is selected and that we have // the active appearance. if (SwtUtil.isChild(child.getControl(), focusControl)) { Assert.isTrue(child == current, "The part with focus is not the selected part"); //$NON-NLS-1$ // focus check commented out since it fails when focus workaround in LayoutPart.setVisible is not present // Assert.isTrue(getActive() == StackPresentation.AS_ACTIVE_FOCUS); } } } // Ensure that "current" points to a valid child if (child == current) { currentFound = true; } // Test the child's internal state child.testInvariants(); } // If we have at least one child, ensure that the "current" pointer points to one of them if (!isDisposed() && getPresentableParts().size() > 0) { Assert.isTrue(currentFound); if (!isDisposed()) { StackPresentation presentation = getPresentation(); // If the presentation controls have focus, ensure that we have the active appearance if (SwtUtil.isChild(presentation.getControl(), focusControl)) { Assert .isTrue( getActive() == StackPresentation.AS_ACTIVE_FOCUS, "The presentation has focus but does not have the active appearance"); //$NON-NLS-1$ } } } // Check to that we're displaying the zoomed icon iff we're actually maximized Assert.isTrue((getState() == IStackPresentationSite.STATE_MAXIMIZED) == (getContainer() != null && getContainer().childIsZoomed(this))); } /* (non-Javadoc) * @see org.eclipse.ui.internal.LayoutPart#describeLayout(java.lang.StringBuffer) */ public void describeLayout(StringBuffer buf) { int activeState = getActive(); if (activeState == StackPresentation.AS_ACTIVE_FOCUS) { buf.append("active "); //$NON-NLS-1$ } else if (activeState == StackPresentation.AS_ACTIVE_NOFOCUS) { buf.append("active_nofocus "); //$NON-NLS-1$ } buf.append("("); //$NON-NLS-1$ LayoutPart[] children = ((ILayoutContainer) this).getChildren(); int visibleChildren = 0; for (int idx = 0; idx < children.length; idx++) { LayoutPart next = children[idx]; if (!(next instanceof PartPlaceholder)) { if (idx > 0) { buf.append(", "); //$NON-NLS-1$ } if (next == requestedCurrent) { buf.append("*"); //$NON-NLS-1$ } next.describeLayout(buf); visibleChildren++; } } buf.append(")"); //$NON-NLS-1$ } /** * See IVisualContainer#add */ public void add(LayoutPart child) { add(child, null); } /** * Add a part at a particular position */ protected void add(LayoutPart newChild, Object cookie) { children.add(newChild); // Fix for bug 78470: if(!(newChild.getContainer() instanceof ContainerPlaceholder)) { newChild.setContainer(this); } showPart(newChild, cookie); } public boolean allowsAdd(LayoutPart toAdd) { return !isStandalone(); } /* * (non-Javadoc) * * @see org.eclipse.ui.internal.ILayoutContainer#allowsAutoFocus() */ public boolean allowsAutoFocus() { if (presentationSite.getState() == IStackPresentationSite.STATE_MINIMIZED) { return false; } return super.allowsAutoFocus(); } /** * @param parts */ protected void close(IPresentablePart[] parts) { for (int idx = 0; idx < parts.length; idx++) { IPresentablePart part = parts[idx]; close(part); } } /** * @param part */ protected void close(IPresentablePart part) { if (!presentationSite.isCloseable(part)) { return; } LayoutPart layoutPart = getPaneFor(part); if (layoutPart != null && layoutPart instanceof PartPane) { PartPane viewPane = (PartPane) layoutPart; viewPane.doHide(); } } public boolean isDisposed() { return getPresentation() == null; } protected AbstractPresentationFactory getFactory() { if (factory != null) { return factory; } return ((WorkbenchWindow) getPage() .getWorkbenchWindow()).getWindowConfigurer() .getPresentationFactory(); } public void createControl(Composite parent) { if (!isDisposed()) { return; } AbstractPresentationFactory factory = getFactory(); PresentationSerializer serializer = new PresentationSerializer( getPresentableParts()); StackPresentation presentation = PresentationFactoryUtil .createPresentation(factory, appearance, parent, presentationSite, serializer, savedPresentationState); createControl(parent, presentation); getControl().moveBelow(null); } // RAP [rh] DnD not supported // /* (non-Javadoc) // * @see org.eclipse.ui.internal.LayoutPart#getDropTarget(java.lang.Object, org.eclipse.swt.graphics.Point) // */ // public IDropTarget getDropTarget(Object draggedObject, Point position) { // // if (!(draggedObject instanceof PartPane)) { // return null; // } // // final PartPane pane = (PartPane) draggedObject; // if (isStandalone() // || !allowsDrop(pane)) { // return null; // } // // // Don't allow views to be dragged between windows // boolean differentWindows = pane.getWorkbenchWindow() != getWorkbenchWindow(); // boolean editorDropOK = ((pane instanceof EditorPane) && // pane.getWorkbenchWindow().getWorkbench() == // getWorkbenchWindow().getWorkbench()); // if (differentWindows && !editorDropOK) { // return null; // } // // StackDropResult dropResult = getPresentation().dragOver( // getControl(), position); // // if (dropResult == null) { // return null; // } // // return createDropTarget(pane, dropResult); // } public void setActive(boolean isActive) { this.isActive = isActive; // Add all visible children to the presentation Iterator iter = children.iterator(); while (iter.hasNext()) { LayoutPart part = (LayoutPart) iter.next(); part.setContainer(isActive ? this : null); } for (Iterator iterator = presentableParts.iterator(); iterator.hasNext();) { PresentablePart next = (PresentablePart) iterator.next(); next.enableInputs(isActive); next.enableOutputs(isActive); } } public void createControl(Composite parent, StackPresentation presentation) { Assert.isTrue(isDisposed()); if (presentationSite.getPresentation() != null) { return; } presentationSite.setPresentation(presentation); // Add all visible children to the presentation // Use a copy of the current set of children to avoid a ConcurrentModificationException // if a part is added to the same stack while iterating over the children (bug 78470) LayoutPart[] childParts = (LayoutPart[]) children.toArray(new LayoutPart[children.size()]); for (int i = 0; i < childParts.length; i++) { LayoutPart part = childParts[i]; showPart(part, null); } if (savedPresentationState!=null) { PresentationSerializer serializer = new PresentationSerializer( getPresentableParts()); presentation.restoreState(serializer, savedPresentationState); } Control ctrl = getPresentation().getControl(); ctrl.setData(this); // We should not have a placeholder selected once we've created the widgetry if (requestedCurrent instanceof PartPlaceholder) { requestedCurrent = null; updateContainerVisibleTab(); } refreshPresentationSelection(); } // RAP [rh] DnD support missing // public IDropTarget createDropTarget(PartPane pane, StackDropResult result) { // dropResult.setTarget(this, pane, result); // return dropResult; // } /** * Saves the current state of the presentation to savedPresentationState, if the * presentation exists. */ protected void savePresentationState() { if (isDisposed()) { return; } {// Save the presentation's state before disposing it XMLMemento memento = XMLMemento .createWriteRoot(IWorkbenchConstants.TAG_PRESENTATION); memento.putString(IWorkbenchConstants.TAG_ID, getFactory().getId()); PresentationSerializer serializer = new PresentationSerializer( getPresentableParts()); getPresentation().saveState(serializer, memento); // Store the memento in savedPresentationState savedPresentationState = memento; } } /** * See LayoutPart#dispose */ public void dispose() { if (isDisposed()) { return; } savePresentationState(); presentationSite.dispose(); for (Iterator iter = presentableParts.iterator(); iter.hasNext();) { PresentablePart part = (PresentablePart) iter.next(); part.dispose(); } presentableParts.clear(); presentationCurrent = null; current = null; fireInternalPropertyChange(PROP_SELECTION); } public void findSashes(LayoutPart part, PartPane.Sashes sashes) { ILayoutContainer container = getContainer(); if (container != null) { container.findSashes(this, sashes); } } /** * Gets the presentation bounds. */ public Rectangle getBounds() { if (getPresentation() == null) { return new Rectangle(0, 0, 0, 0); } return getPresentation().getControl().getBounds(); } /** * See IVisualContainer#getChildren */ public LayoutPart[] getChildren() { return (LayoutPart[]) children.toArray(new LayoutPart[children.size()]); } public Control getControl() { StackPresentation presentation = getPresentation(); if (presentation == null) { return null; } return presentation.getControl(); } /** * Answer the number of children. */ public int getItemCount() { if (isDisposed()) { return children.size(); } return getPresentableParts().size(); } /** * Returns the LayoutPart for the given IPresentablePart, or null if the given * IPresentablePart is not in this stack. Returns null if given a null argument. * * @param part to locate or null * @return */ protected LayoutPart getPaneFor(IPresentablePart part) { if (part == null || !(part instanceof PresentablePart)) { return null; } return ((PresentablePart)part).getPane(); } /** * Get the parent control. */ public Composite getParent() { return getControl().getParent(); } /** * Returns a list of IPresentablePart * * @return */ public List getPresentableParts() { return presentableParts; } private PresentablePart getPresentablePart(LayoutPart pane) { for (Iterator iter = presentableParts.iterator(); iter.hasNext();) { PresentablePart part = (PresentablePart) iter.next(); if (part.getPane() == pane) { return part; } } return null; } protected StackPresentation getPresentation() { return presentationSite.getPresentation(); } /** * Returns the visible child. * @return the currently visible part, or null if none */ public PartPane getSelection() { if (current instanceof PartPane) { return (PartPane) current; } return null; } private void presentationSelectionChanged(IPresentablePart newSelection) { // Ignore selection changes that occur as a result of removing a part if (ignoreSelectionChanges) { return; } LayoutPart newPart = getPaneFor(newSelection); // This method should only be called on objects that are already in the layout Assert.isNotNull(newPart); if (newPart != requestedCurrent) { setSelection(newPart); } newPart.setFocus(); } /** * See IVisualContainer#remove */ public void remove(LayoutPart child) { PresentablePart presentablePart = getPresentablePart(child); // Need to remove it from the list of children before notifying the presentation // since it may setVisible(false) on the part, leading to a partHidden notification, // during which findView must not find the view being removed. See bug 60039. children.remove(child); StackPresentation presentation = getPresentation(); if (presentablePart != null && presentation != null) { ignoreSelectionChanges = true; presentableParts .remove(presentablePart); presentation.removePart(presentablePart); presentablePart.dispose(); ignoreSelectionChanges = false; } if (!isDisposed()) { child.setContainer(null); } if (child == requestedCurrent) { updateContainerVisibleTab(); } } /** * Reparent a part. Also reparent visible children... */ public void reparent(Composite newParent) { Control control = getControl(); if ((control == null) || (control.getParent() == newParent) || !control.isReparentable()) { return; } super.reparent(newParent); Iterator iter = children.iterator(); while (iter.hasNext()) { LayoutPart next = (LayoutPart) iter.next(); next.reparent(newParent); } } /** * See IVisualContainer#replace */ public void replace(LayoutPart oldChild, LayoutPart newChild) { int idx = children.indexOf(oldChild); int numPlaceholders = 0; //subtract the number of placeholders still existing in the list //before this one - they wont have parts. for (int i = 0; i < idx; i++) { if (children.get(i) instanceof PartPlaceholder) { numPlaceholders++; } } Integer cookie = new Integer(idx - numPlaceholders); children.add(idx, newChild); showPart(newChild, cookie); if (oldChild == requestedCurrent && !(newChild instanceof PartPlaceholder)) { setSelection(newChild); } remove(oldChild); } /* (non-Javadoc) * @see org.eclipse.ui.internal.LayoutPart#computePreferredSize(boolean, int, int, int) */ public int computePreferredSize(boolean width, int availableParallel, int availablePerpendicular, int preferredParallel) { return getPresentation().computePreferredSize(width, availableParallel, availablePerpendicular, preferredParallel); } /* (non-Javadoc) * @see org.eclipse.ui.internal.LayoutPart#getSizeFlags(boolean) */ public int getSizeFlags(boolean horizontal) { StackPresentation presentation = getPresentation(); if (presentation != null) { return presentation.getSizeFlags(horizontal); } return 0; } /** * @see IPersistable */ public IStatus restoreState(IMemento memento) { // Read the active tab. String activeTabID = memento .getString(IWorkbenchConstants.TAG_ACTIVE_PAGE_ID); // Read the page elements. IMemento[] children = memento.getChildren(IWorkbenchConstants.TAG_PAGE); if (children != null) { // Loop through the page elements. for (int i = 0; i < children.length; i++) { // Get the info details. IMemento childMem = children[i]; String partID = childMem .getString(IWorkbenchConstants.TAG_CONTENT); // Create the part. LayoutPart part = new PartPlaceholder(partID); part.setContainer(this); add(part); //1FUN70C: ITPUI:WIN - Shouldn't set Container when not active //part.setContainer(this); if (partID.equals(activeTabID)) { setSelection(part); // Mark this as the active part. //current = part; } } } IPreferenceStore preferenceStore = PrefUtil.getAPIPreferenceStore(); boolean useNewMinMax = preferenceStore.getBoolean(IWorkbenchPreferenceConstants.ENABLE_NEW_MIN_MAX); final Integer expanded = memento.getInteger(IWorkbenchConstants.TAG_EXPANDED); if (useNewMinMax && expanded != null) { StartupThreading.runWithoutExceptions(new StartupRunnable() { public void runWithException() throws Throwable { setState((expanded == null || expanded.intValue() != IStackPresentationSite.STATE_MINIMIZED) ? IStackPresentationSite.STATE_RESTORED : IStackPresentationSite.STATE_MINIMIZED); } }); } else { setState((expanded == null || expanded.intValue() != IStackPresentationSite.STATE_MINIMIZED) ? IStackPresentationSite.STATE_RESTORED : IStackPresentationSite.STATE_MINIMIZED); } Integer appearance = memento .getInteger(IWorkbenchConstants.TAG_APPEARANCE); if (appearance != null) { this.appearance = appearance.intValue(); } // Determine if the presentation has saved any info here savedPresentationState = null; IMemento[] presentationMementos = memento .getChildren(IWorkbenchConstants.TAG_PRESENTATION); for (int idx = 0; idx < presentationMementos.length; idx++) { IMemento child = presentationMementos[idx]; String id = child.getString(IWorkbenchConstants.TAG_ID); if (Util.equals(id, getFactory().getId())) { savedPresentationState = child; break; } } IMemento propertiesState = memento.getChild(IWorkbenchConstants.TAG_PROPERTIES); if (propertiesState != null) { IMemento[] props = propertiesState.getChildren(IWorkbenchConstants.TAG_PROPERTY); for (int i = 0; i < props.length; i++) { properties.put(props[i].getID(), props[i].getTextData()); } } return Status.OK_STATUS; } /* (non-Javadoc) * @see org.eclipse.ui.internal.LayoutPart#setVisible(boolean) */ public void setVisible(boolean makeVisible) { Control ctrl = getControl(); boolean useShortcut = makeVisible || !isActive; if (!SwtUtil.isDisposed(ctrl) && useShortcut) { if (makeVisible == ctrl.getVisible()) { return; } } if (makeVisible) { for (Iterator iterator = presentableParts.iterator(); iterator.hasNext();) { PresentablePart next = (PresentablePart) iterator.next(); next.enableInputs(isActive); next.enableOutputs(isActive); } } super.setVisible(makeVisible); StackPresentation presentation = getPresentation(); if (presentation != null) { presentation.setVisible(makeVisible); } if (!makeVisible) { for (Iterator iterator = presentableParts.iterator(); iterator.hasNext();) { PresentablePart next = (PresentablePart) iterator.next(); next.enableInputs(false); } } } /** * @see IPersistable */ public IStatus saveState(IMemento memento) { // Save the active tab. if (requestedCurrent != null) { memento.putString(IWorkbenchConstants.TAG_ACTIVE_PAGE_ID, requestedCurrent .getCompoundId()); } // Write out the presentable parts (in order) Set cachedIds = new HashSet(); Iterator ppIter = getPresentableParts().iterator(); while (ppIter.hasNext()) { PresentablePart presPart = (PresentablePart) ppIter.next(); IMemento childMem = memento .createChild(IWorkbenchConstants.TAG_PAGE); PartPane part = presPart.getPane(); String tabText = part.getPartReference().getPartName(); childMem.putString(IWorkbenchConstants.TAG_LABEL, tabText); childMem.putString(IWorkbenchConstants.TAG_CONTENT, presPart.getPane().getPlaceHolderId()); // Cache the id so we don't write it out later cachedIds.add(presPart.getPane().getPlaceHolderId()); } Iterator iter = children.iterator(); while (iter.hasNext()) { LayoutPart next = (LayoutPart) iter.next(); PartPane part = null; if (next instanceof PartPane) { // Have we already written it out? if (cachedIds.contains(((PartPane)next).getPlaceHolderId())) continue; part = (PartPane)next; } IMemento childMem = memento .createChild(IWorkbenchConstants.TAG_PAGE); String tabText = "LabelNotFound"; //$NON-NLS-1$ if (part != null) { tabText = part.getPartReference().getPartName(); } childMem.putString(IWorkbenchConstants.TAG_LABEL, tabText); childMem.putString(IWorkbenchConstants.TAG_CONTENT, next .getCompoundId()); } IPreferenceStore preferenceStore = PrefUtil.getAPIPreferenceStore(); boolean useNewMinMax = preferenceStore.getBoolean(IWorkbenchPreferenceConstants.ENABLE_NEW_MIN_MAX); if (useNewMinMax) { memento.putInteger(IWorkbenchConstants.TAG_EXPANDED, presentationSite.getState()); } else { memento .putInteger( IWorkbenchConstants.TAG_EXPANDED, (presentationSite.getState() == IStackPresentationSite.STATE_MINIMIZED) ? IStackPresentationSite.STATE_MINIMIZED : IStackPresentationSite.STATE_RESTORED); } memento.putInteger(IWorkbenchConstants.TAG_APPEARANCE, appearance); savePresentationState(); if (savedPresentationState != null) { IMemento presentationState = memento .createChild(IWorkbenchConstants.TAG_PRESENTATION); presentationState.putMemento(savedPresentationState); } if (!properties.isEmpty()) { IMemento propertiesState = memento.createChild(IWorkbenchConstants.TAG_PROPERTIES); Set ids = properties.keySet(); for (Iterator iterator = ids.iterator(); iterator.hasNext();) { String id = (String)iterator.next(); if (properties.get(id) == null) continue; IMemento prop = propertiesState.createChild(IWorkbenchConstants.TAG_PROPERTY, id); prop.putTextData((String)properties.get(id)); } } return Status.OK_STATUS; } protected WorkbenchPage getPage() { WorkbenchWindow window = (WorkbenchWindow) getWorkbenchWindow(); if (window == null) { return null; } return (WorkbenchPage) window.getActivePage(); } /** * Set the active appearence on the tab folder. * * @param active */ public void setActive(int activeState) { if (activeState == StackPresentation.AS_ACTIVE_FOCUS && isMinimized) { setMinimized(false); } presentationSite.setActive(activeState); } public int getActive() { return presentationSite.getActive(); } /** * Sets the presentation bounds. */ public void setBounds(Rectangle r) { if (getPresentation() != null) { getPresentation().setBounds(r); } } public void setSelection(LayoutPart part) { if (part == requestedCurrent) { return; } requestedCurrent = part; refreshPresentationSelection(); } /** * Subclasses should override this method to update the enablement state of their * actions */ protected abstract void updateActions(PresentablePart current); /* (non-Javadoc) * @see org.eclipse.ui.internal.LayoutPart#handleDeferredEvents() */ protected void handleDeferredEvents() { super.handleDeferredEvents(); refreshPresentationSelection(); } private void refreshPresentationSelection() { // If deferring UI updates, exit. if (isDeferred()) { return; } // If the presentation is already displaying the desired part, then there's nothing // to do. if (current == requestedCurrent) { return; } StackPresentation presentation = getPresentation(); if (presentation != null) { presentationCurrent = getPresentablePart(requestedCurrent); if (!isDisposed()) { updateActions(presentationCurrent); } if (presentationCurrent != null && presentation != null) { requestedCurrent.createControl(getParent()); if (requestedCurrent.getControl().getParent() != getControl() .getParent()) { requestedCurrent.reparent(getControl().getParent()); } presentation.selectPart(presentationCurrent); } // Update the return value of getVisiblePart current = requestedCurrent; fireInternalPropertyChange(PROP_SELECTION); } } public int getState() { return presentationSite.getState(); } /** * Sets the minimized state for this stack. The part may call this method to * minimize or restore itself. The minimized state only affects the view * when unzoomed in the 3.0 presentation (in 3.3 it's handled by the * ViewStack directly and works as expected). */ public void setMinimized(boolean minimized) { if (minimized != isMinimized) { isMinimized = minimized; refreshPresentationState(); } } /* (non-Javadoc) * @see org.eclipse.ui.internal.ILayoutContainer#obscuredByZoom(org.eclipse.ui.internal.LayoutPart) */ public boolean childObscuredByZoom(LayoutPart toTest) { return isObscuredByZoom(); } /* (non-Javadoc) * @see org.eclipse.ui.internal.LayoutPart#requestZoom(org.eclipse.ui.internal.LayoutPart) */ public void childRequestZoomIn(LayoutPart toZoom) { super.childRequestZoomIn(toZoom); requestZoomIn(); } /* (non-Javadoc) * @see org.eclipse.ui.internal.LayoutPart#requestZoomOut() */ public void childRequestZoomOut() { super.childRequestZoomOut(); requestZoomOut(); } /* (non-Javadoc) * @see org.eclipse.ui.internal.ILayoutContainer#isZoomed(org.eclipse.ui.internal.LayoutPart) */ public boolean childIsZoomed(LayoutPart toTest) { return isZoomed(); } /** * This is a hack that allows us to preserve the old * min/max behavior for the stack containing the IntroPart. * This is required to have the initial Intro (Welcome) * pane to show correctly but will induce strange * effects should a user re-locate the part to * stacks other that its initial one... * * @return true if the stack contains the intro * as a ViewPane (not if it's only a placeholder) */ private boolean isIntroInStack() { LayoutPart[] kids = getChildren(); for (int i = 0; i < kids.length; i++) { if (kids[i] instanceof ViewPane) { ViewPane vp = (ViewPane) kids[i]; if (vp.getID().equals(IIntroConstants.INTRO_VIEW_ID)) return true; } } return false; } private void smartZoom() { WorkbenchWindow wbw = (WorkbenchWindow) getPage().getWorkbenchWindow(); if (wbw == null || wbw.getShell() == null) return; Perspective perspective = getPage().getActivePerspective(); FastViewManager fvm = perspective.getFastViewManager(); fvm.deferUpdates(true); // Cache the layout bounds perspective.getPresentation().updateBoundsMap(); LayoutPart[] children = perspective.getPresentation().getLayout().getChildren(); for (int i = 0; i < children.length; i++) { if (children[i] != this) { if (children[i] instanceof ViewStack) { ((ViewStack) children[i]).setMinimized(true); ViewStackTrimToolBar vstb = fvm .getViewStackTrimToolbar(children[i] .getID()); vstb.setRestoreOnUnzoom(true); } else if (children[i] instanceof EditorSashContainer && !(this instanceof EditorStack)) { perspective.setEditorAreaState(IStackPresentationSite.STATE_MINIMIZED); perspective.setEditorAreaRestoreOnUnzoom(true); } } } // If the editor area has changed state tell the perspective if (this instanceof EditorStack) perspective.setEditorAreaState(IStackPresentationSite.STATE_MAXIMIZED); // Clear the boundsMap perspective.getPresentation().resetBoundsMap(); // We're done batching... fvm.deferUpdates(false); perspective.getPresentation().setMaximizedStack(this); smartZoomed = true; } protected void smartUnzoom() { // Prevent recursion through 'setMinimized' if (doingUnzoom) return; doingUnzoom = true; WorkbenchWindow wbw = (WorkbenchWindow) getPage().getWorkbenchWindow(); if (wbw == null || wbw.getShell() == null) return; ITrimManager tbm = wbw.getTrimManager(); Perspective perspective = getPage().getActivePerspective(); FastViewManager fvm = perspective.getFastViewManager(); ILayoutContainer root = getContainer(); // We go up one more level when maximizing an editor stack // so that we 'zoom' the editor area boolean restoringEditorArea = false; if (root instanceof EditorSashContainer) { root = ((EditorSashContainer) root).getContainer(); restoringEditorArea = true; } // This is a compound operation fvm.deferUpdates(true); LayoutPart[] children = root.getChildren(); for (int i = 0; i < children.length; i++) { if (children[i] != this) { IWindowTrim trim = tbm.getTrim(children[i].getID()); if (trim == null) continue; if (trim instanceof ViewStackTrimToolBar) { ViewStackTrimToolBar vstb = (ViewStackTrimToolBar) trim; if (vstb.restoreOnUnzoom() && children[i] instanceof ContainerPlaceholder) { // In the current presentation its a // container placeholder ViewStack realStack = (ViewStack) ((ContainerPlaceholder) children[i]) .getRealContainer(); realStack.setMinimized(false); vstb.setRestoreOnUnzoom(false); } } else if (trim instanceof EditorAreaTrimToolBar) { if (perspective.getEditorAreaRestoreOnUnzoom()) perspective.setEditorAreaState(IStackPresentationSite.STATE_RESTORED); } } } // If the editor area has changed state tell the perspective if (restoringEditorArea) perspective.setEditorAreaState(IStackPresentationSite.STATE_RESTORED); perspective.getPresentation().setMaximizedStack(null); fvm.deferUpdates(false); smartZoomed = false; doingUnzoom = false; } protected void setState(final int newState) { final int oldState = presentationSite.getState(); if (!supportsState(newState) || newState == oldState) { return; } final WorkbenchWindow wbw = (WorkbenchWindow) getPage().getWorkbenchWindow(); if (wbw == null || wbw.getShell() == null || wbw.getActiveWorkbenchPage() == null) return; WorkbenchPage page = wbw.getActiveWorkbenchPage(); if (page == null) return; boolean useNewMinMax = Perspective.useNewMinMax(page.getActivePerspective()); // we have to fiddle with the zoom behavior to satisfy Intro req's // by using the old zoom behavior for its stack if (newState == IStackPresentationSite.STATE_MAXIMIZED) useNewMinMax = useNewMinMax && !isIntroInStack(); else if (newState == IStackPresentationSite.STATE_RESTORED) { PartStack maxStack = page.getActivePerspective().getPresentation().getMaximizedStack(); useNewMinMax = useNewMinMax && (maxStack == this || maxStack instanceof EditorStack); } if (useNewMinMax) { StartupThreading.runWithoutExceptions(new StartupRunnable() { public void runWithException() throws Throwable { wbw.getPageComposite().setRedraw(false); try { if (newState == IStackPresentationSite.STATE_MAXIMIZED) { smartZoom(); } else if (oldState == IStackPresentationSite.STATE_MAXIMIZED) { smartUnzoom(); } if (newState == IStackPresentationSite.STATE_MINIMIZED) { setMinimized(true); } } finally { wbw.getPageComposite().setRedraw(true); // Force a redraw (fixes Mac refresh) wbw.getShell().redraw(); } setPresentationState(newState); } }); } else { boolean minimized = (newState == IStackPresentationSite.STATE_MINIMIZED); setMinimized(minimized); if (newState == IStackPresentationSite.STATE_MAXIMIZED) { requestZoomIn(); } else if (oldState == IStackPresentationSite.STATE_MAXIMIZED) { requestZoomOut(); if (newState == IStackPresentationSite.STATE_MINIMIZED) setMinimized(true); } } } /** * Called by the workbench page to notify this part that it has been zoomed or unzoomed. * The PartStack should not call this method itself -- it must request zoom changes by * talking to the WorkbenchPage. */ public void setZoomed(boolean isZoomed) { super.setZoomed(isZoomed); LayoutPart[] children = getChildren(); for (int i = 0; i < children.length; i++) { LayoutPart next = children[i]; next.setZoomed(isZoomed); } refreshPresentationState(); } public boolean isZoomed() { ILayoutContainer container = getContainer(); if (container != null) { return container.childIsZoomed(this); } return false; } protected void refreshPresentationState() { if (isZoomed() || smartZoomed) { presentationSite.setPresentationState(IStackPresentationSite.STATE_MAXIMIZED); } else { boolean wasMinimized = (presentationSite.getState() == IStackPresentationSite.STATE_MINIMIZED); if (isMinimized) { presentationSite.setPresentationState(IStackPresentationSite.STATE_MINIMIZED); } else { presentationSite.setPresentationState(IStackPresentationSite.STATE_RESTORED); } if (isMinimized != wasMinimized) { flushLayout(); if (isMinimized) { WorkbenchPage page = getPage(); if (page != null) { page.refreshActiveView(); } } } } } /** * Makes the given part visible in the presentation. * @param part the part to add to the stack * @param cookie other information */ private void showPart(LayoutPart part, Object cookie) { if (isDisposed()) { return; } if ((part instanceof PartPlaceholder)) { part.setContainer(this); return; } if (!(part instanceof PartPane)) { WorkbenchPlugin.log(NLS.bind( WorkbenchMessages.get().PartStack_incorrectPartInFolder, part .getID())); return; } PartPane pane = (PartPane)part; PresentablePart presentablePart = new PresentablePart(pane, getControl().getParent()); presentableParts.add(presentablePart); if (isActive) { part.setContainer(this); // The active part should always be enabled if (part.getControl() != null) part.getControl().setEnabled(true); } presentationSite.getPresentation().addPart(presentablePart, cookie); if (requestedCurrent == null) { setSelection(part); } if (childObscuredByZoom(part)) { presentablePart.enableInputs(false); } } /** * Update the container to show the correct visible tab based on the * activation list. */ private void updateContainerVisibleTab() { LayoutPart[] parts = getChildren(); if (parts.length < 1) { setSelection(null); return; } PartPane selPart = null; int topIndex = 0; WorkbenchPage page = getPage(); if (page != null) { IWorkbenchPartReference sortedPartsArray[] = page.getSortedParts(); List sortedParts = Arrays.asList(sortedPartsArray); for (int i = 0; i < parts.length; i++) { if (parts[i] instanceof PartPane) { IWorkbenchPartReference part = ((PartPane) parts[i]) .getPartReference(); int index = sortedParts.indexOf(part); if (index >= topIndex) { topIndex = index; selPart = (PartPane) parts[i]; } } } } if (selPart == null) { List presentableParts = getPresentableParts(); if (presentableParts.size() != 0) { IPresentablePart part = (IPresentablePart) getPresentableParts() .get(0); selPart = (PartPane) getPaneFor(part); } } setSelection(selPart); } /** * */ public void showSystemMenu() { getPresentation().showSystemMenu(); } public void showPaneMenu() { getPresentation().showPaneMenu(); } public void showPartList() { getPresentation().showPartList(); } public Control[] getTabList(LayoutPart part) { if (part != null) { IPresentablePart presentablePart = getPresentablePart(part); StackPresentation presentation = getPresentation(); if (presentablePart != null && presentation != null) { return presentation.getTabList(presentablePart); } } return new Control[0]; } /** * * @param beingDragged * @param initialLocation * @param keyboard */ private void dragStart(IPresentablePart beingDragged, Point initialLocation, boolean keyboard) { if (beingDragged == null) { paneDragStart((LayoutPart)null, initialLocation, keyboard); } else { if (presentationSite.isPartMoveable(beingDragged)) { LayoutPart pane = getPaneFor(beingDragged); if (pane != null) { paneDragStart(pane, initialLocation, keyboard); } } } } public void paneDragStart(LayoutPart pane, Point initialLocation, boolean keyboard) { if (pane == null) { if (canMoveFolder()) { if (presentationSite.getState() == IStackPresentationSite.STATE_MAXIMIZED) { // Calculate where the initial location was BEFORE the 'restore'...as a percentage Rectangle bounds = Geometry.toDisplay(getParent(), getPresentation().getControl().getBounds()); float xpct = (initialLocation.x - bounds.x) / (float)(bounds.width); float ypct = (initialLocation.y - bounds.y) / (float)(bounds.height); // Only restore if we're dragging views/view stacks if (this instanceof ViewStack) setState(IStackPresentationSite.STATE_RESTORED); // Now, adjust the initial location to be within the bounds of the restored rect bounds = Geometry.toDisplay(getParent(), getPresentation().getControl().getBounds()); initialLocation.x = (int) (bounds.x + (xpct * bounds.width)); initialLocation.y = (int) (bounds.y + (ypct * bounds.height)); } // RAP [rh] DnD not supported // DragUtil.performDrag(PartStack.this, Geometry // .toDisplay(getParent(), getPresentation().getControl() // .getBounds()), initialLocation, !keyboard); } } else { if (presentationSite.getState() == IStackPresentationSite.STATE_MAXIMIZED) { // Calculate where the initial location was BEFORE the 'restore'...as a percentage Rectangle bounds = Geometry.toDisplay(getParent(), getPresentation().getControl().getBounds()); float xpct = (initialLocation.x - bounds.x) / (float)(bounds.width); float ypct = (initialLocation.y - bounds.y) / (float)(bounds.height); // Only restore if we're dragging views/view stacks if (this instanceof ViewStack) setState(IStackPresentationSite.STATE_RESTORED); // Now, adjust the initial location to be within the bounds of the restored rect // See bug 100908 bounds = Geometry.toDisplay(getParent(), getPresentation().getControl().getBounds()); initialLocation.x = (int) (bounds.x + (xpct * bounds.width)); initialLocation.y = (int) (bounds.y + (ypct * bounds.height)); } // RAP [rh] DnD not supported // DragUtil.performDrag(pane, Geometry.toDisplay(getParent(), // getPresentation().getControl().getBounds()), // initialLocation, !keyboard); } } /** * @return Returns the savedPresentationState. */ public IMemento getSavedPresentationState() { return savedPresentationState; } private void fireInternalPropertyChange(int id) { Object listeners[] = this.listeners.getListeners(); for (int i = 0; i < listeners.length; i++) { ((IPropertyListener) listeners[i]).propertyChanged(this, id); } } // TrimStack Support /** * Explicitly sets the presentation state. This is used by the * new min/max code to force the CTabFolder to show the proper * state without going through the 'setState' code (which causes * nasty side-effects. * @param newState The state to set the presentation to */ public void setPresentationState(int newState) { presentationSite.setPresentationState(newState); } // // Support for passing perspective layout properties to the presentation public String getProperty(String id) { return (String)properties.get(id); } public void setProperty(String id, String value) { if (value==null) { properties.remove(id); } else { properties.put(id, value); } } /** * Copies all appearance related data from this stack to the given stack. */ public void copyAppearanceProperties(PartStack copyTo) { copyTo.appearance = this.appearance; if (!properties.isEmpty()) { Set ids = properties.keySet(); for (Iterator iterator = ids.iterator(); iterator.hasNext();) { String id = (String)iterator.next(); copyTo.setProperty(id, (String)properties.get(id)); } } } }